In [2]:
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
In [3]:
from sklearn.decomposition import PCA
from sklearn import decomposition
from sklearn.preprocessing import StandardScaler
from sklearn import preprocessing
In [4]:
df=pd.read_excel('/content/base_a_traiter.xlsx')
In [ ]:
df = pd.read_excel('C:\\Users\\elisa\\OneDrive - GENES\\Documents\\GitHub\\Projet-Python-pour-le-Data-Scientist\\base_a_traiter.xlsx')
In [ ]:
df.head()
Out[ ]:
Compagnie Total d'escales Destination Distance Date_aller Date_retour Prix Duree de vol
0 Air France 0 Oslo 1344.0 2022-04-01 2022-04-08 204 2.333333
1 Air France 0 Oslo 1344.0 2022-04-01 2022-04-08 204 2.500000
2 Flyr AS 0 Oslo 1344.0 2022-04-01 2022-04-08 195 2.416667
3 Flyr AS 0 Oslo 1344.0 2022-04-01 2022-04-08 195 2.416667
4 Norwegian 0 Oslo 1344.0 2022-04-01 2022-04-08 186 2.333333
In [ ]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 73278 entries, 0 to 73277
Data columns (total 8 columns):
 #   Column           Non-Null Count  Dtype         
---  ------           --------------  -----         
 0   Compagnie        73263 non-null  object        
 1   Total d'escales  73278 non-null  int64         
 2   Destination      73278 non-null  object        
 3   Distance         73278 non-null  float64       
 4   Date_aller       73278 non-null  datetime64[ns]
 5   Date_retour      73278 non-null  datetime64[ns]
 6   Prix             73278 non-null  int64         
 7   Duree de vol     73278 non-null  float64       
dtypes: datetime64[ns](2), float64(2), int64(2), object(2)
memory usage: 4.5+ MB

1.Chiffres généraux

In [ ]:
df.describe().transpose()
Out[ ]:
count mean std min 25% 50% 75% max
Total d'escales 73278.0 0.516076 0.520924 0.000000 0.000000 1.0 1.00 2.00
Distance 73278.0 1281.930674 667.682119 342.760000 1054.000000 1054.0 2095.52 2486.34
Prix 73278.0 278.167663 312.885063 33.000000 148.000000 203.0 308.00 5355.00
Duree de vol 73278.0 4.469546 2.962306 1.083333 2.083333 4.0 6.00 28.25
In [ ]:
plt.figure(figsize=(12,8))
p= sns.distplot(df['Prix'])
p.set_title("Répartition des différents prix dans les données")
C:\Users\elisa\anaconda3\lib\site-packages\seaborn\distributions.py:2557: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms).
  warnings.warn(msg, FutureWarning)
Out[ ]:
Text(0.5, 1.0, 'Répartition des différents prix dans les données')

Les prix de billets d'avion sont en très grande majorité entre 0 et 1000 €

2. Corrélation entre les variables

i) Corrélation avec le prix

In [ ]:
fig, axs = plt.subplots(3, 1, figsize=(10,15))
fig.suptitle("Relation entre nos variables quantitatives et le prix du billet d'avion")

test00=df.groupby('Distance',as_index=False).mean()
axs[0].scatter(test00['Distance'], test00['Prix'])
axs[0].set(xlabel='distance entre Paris et la ville de destination', ylabel='Prix selon la distance parcouru')

test01=df.groupby('Duree de vol',as_index=False).mean()
axs[1].scatter(test01['Duree de vol'], test01['Prix'])
axs[1].set(xlabel='Duree du trajet', ylabel='Prix selon la duree du vol')

test02=df.groupby("Total d'escales",as_index=False).mean()
axs[2].scatter(test02["Total d'escales"], test02['Prix'])
axs[2].set(xlabel="Nombre d'escales", ylabel="Prix selon le nombre d'escales durant le trajet")
Out[ ]:
[Text(0.5, 0, "Nombre d'escales"),
 Text(0, 0.5, "Prix selon le nombre d'escales durant le trajet")]

Selon ces graphiques, nous pouvons constater que :

• il semblerait que la distance qui sépare Paris et la ville de destination ait une influence sur le prix

• La durée du trajet et le nombre d'escales ne semblent pas beaucoup influencer le prix

Dans les prochaines sections nous poussons notre analyse avec la matrice de corrélation et l'analyse en composante principale (PCA)

ii) Matrice de corrélation

In [ ]:
non_top_10_perc= df.sort_values('Prix', ascending= False).iloc[6133:] 
## data sans les données dont le prix était dans les 1% les plus chers
In [ ]:
fig, axes = plt.subplots(ncols=2, figsize=(16, 4))

ax1, ax2 = axes

im1 = sns.heatmap(df.corr(), cmap= sns.color_palette("viridis", as_cmap=True), annot = True, ax = ax1)
im2 = sns.heatmap(data=non_top_10_perc.iloc[:, 1:10].corr(), cmap = 'viridis', annot=True, ax = ax2)

im1.set_title("Matrice de corrélation des variables quantitatives")
im2.set_title("Sans les 1% des prix les plus chers")
Out[ ]:
Text(0.5, 1.0, 'Sans les 1% des prix les plus chers')

En éliminant ces valeurs extrêmes on obtient:

• toujours une grande corrélation entre le nombre total d'escales et la durée du vol

• une plus grande corrélation entre la distance et le prix du billet d'avion ce qui est assez intuitif!;

• une corrélation positive mais certes faible entre la distance et le nombre total d'escales ;

• d'une corrélation positive entre la durée du vol et la distance du trajet ce qui est normal;

•d'une faible corrélation positive entre la durée du vol et le prix

•d'une faible corrélation positive entre le nombre total d'escales et le prix

iii) ACP

Afin de visualiser en même temps tous nos variables quantitatives( Total d'escales, Duree de vol, Prix, Distance) sur un plan, la solution est d’effectuer une projection orthogonale. Malheureusement, quand on projette des points, on perd de l’information.

C’est ici la clé de l’ACP : rechercher la projection pour laquelle l’inertie des points est maximale.

Nous proposons d'afficher les cercles de corrélation pour regarder quels sont les variables qui sont le plus corrélés avec le prix du billet d'avion.

In [5]:
#Fonctions pour la représentation du cercle de corrélation

def display_circles(pcs, n_comp, pca, axis_ranks, labels=None, label_rotation=0, varsup=None, lims=None):
    for d1, d2 in axis_ranks: # On affiche les 2 premières composantes
        if d2 < n_comp:

            # initialisation de la figure
            fig, ax = plt.subplots(figsize=(7,6))

            # détermination des limites du graphique
            if lims is not None :
                xmin, xmax, ymin, ymax = lims
            elif pcs.shape[1] < 30 :
                xmin, xmax, ymin, ymax = -1, 1, -1, 1
            else :
                xmin, xmax, ymin, ymax = min(pcs[d1,:]), max(pcs[d1,:]), min(pcs[d2,:]), max(pcs[d2,:])

            # affichage des flèches
            # s'il y a plus de 30 flèches, on n'affiche pas le triangle à leur extrémité
            if pcs.shape[1] < 30 :
                plt.quiver(np.zeros(pcs.shape[1]), np.zeros(pcs.shape[1]),
                   pcs[d1,:], pcs[d2,:], 
                   angles='xy', scale_units='xy', scale=1, color="grey")
                # (voir la doc : https://matplotlib.org/api/_as_gen/matplotlib.pyplot.quiver.html)
            else:
                lines = [[[0,0],[x,y]] for x,y in pcs[[d1,d2]].T]
                ax.add_collection(LineCollection(lines, axes=ax, alpha=.1, color='black'))
            
            # affichage des noms des variables  
            if labels is not None:  
                for i,(x, y) in enumerate(pcs[[d1,d2]].T):
                    if x >= xmin and x <= xmax and y >= ymin and y <= ymax :
                        plt.text(x, y, labels[i], fontsize='14', ha='center', va='center', rotation=label_rotation, color="blue", alpha=0.5)
            
            # affichage du cercle
            circle = plt.Circle((0,0), 1, facecolor='none', edgecolor='b')
            plt.gca().add_artist(circle)

            #variables illustratives
            if varsup is not None:
               corsup = np.zeros(n_comp)
               for j in range(n_comp):
                  corsup[j] = np.corrcoef(varsup,X_projected[:,j])[0,1]
               plt.annotate(varsup.name,(corsup[d1-1],corsup[d2-1]),color='g')
  

            # définition des limites du graphique
            plt.xlim(xmin, xmax)
            plt.ylim(ymin, ymax)
        
            # affichage des lignes horizontales et verticales
            plt.plot([-1, 1], [0, 0], color='grey', ls='--')
            plt.plot([0, 0], [-1, 1], color='grey', ls='--')

            # nom des axes, avec le pourcentage d'inertie expliqué
            plt.xlabel('F{} ({}%)'.format(d1+1, round(100*pca.explained_variance_ratio_[d1],1)))
            plt.ylabel('F{} ({}%)'.format(d2+1, round(100*pca.explained_variance_ratio_[d2],1)))

            plt.title("Cercle des corrélations (F{} et F{})".format(d1+1, d2+1))
            plt.show(block=False)
In [8]:
from sklearn.decomposition import PCA
from sklearn import decomposition
from sklearn.preprocessing import StandardScaler
from sklearn import preprocessing
# choix du nombre de composantes à calculer
n_comp = 2

mycolumns = ["Total d'escales" , 'Duree de vol', 'Distance']

df_pca = df[mycolumns]

# préparation des données pour l'ACP
df_pca = df_pca.fillna(df_pca.mean()) # Il est fréquent de remplacer les valeurs inconnues par la moyenne de la variable
X = df_pca.values
names = df.index #pour avoir les intitulés

#transformation – centrage-réduction
sc = StandardScaler()
Z = sc.fit_transform(X)

# Calcul des composantes principales
acp = PCA(svd_solver='full')
coord = acp.fit_transform(Z)

#vérification
#moyenne
print(np.mean(Z,axis=0))
#écart-type
print(np.std(Z,axis=0,ddof=0))
[ 1.02395416e-16  4.96462623e-17 -2.23408180e-16]
[1. 1. 1.]
In [9]:
#Coordonnées de la varaible supplementaire
varsup=df['Prix']
corsup = np.zeros(X.shape[1])
X_projected = acp.transform(Z)
for j in range(X.shape[1]):
  corsup[j] = np.corrcoef(varsup,X_projected[:,j])[0,1]

#affichage des corrélations avec les axes
print(corsup)
[ 0.2326989   0.20068072 -0.06018879]
In [10]:
# Cercle des corrélations

pcs = acp.components_

display_circles(pcs, X.shape[1], acp, [(0,1)],labels = np.array(mycolumns),varsup=df['Prix'])

La distance est bien représentée et positivement corrélée sur l'axe 2. La durée et le total d'escales sont positivement corrélées à l'axe 1 et négativement corrélées à l'axe 2, ils sont aussi mieux répresenter sur l'axe 1 (les flèches sont plus longues sur l'axe 1 que sur l'axe 2).

L'autre point à soulever ici est que les 2 premières dimension contiennent 93% de l'inertie totale (l’inertie est la variance totale du tableau de données, i.e. la trace de la matrice des corrélations)

3. Caractérisation de chaque destination en fonction des variables quantitatives

i) Diagramme en barre

In [ ]:
df_moyen= df.groupby(['Destination']).mean()
In [ ]:
df_moyen
Out[ ]:
Total d'escales Distance Prix Duree de vol
Destination
Athenes 0.742656 2095.52 287.960344 6.443462
Londres 0.350297 342.76 173.448249 2.475336
Madrid 0.400604 1054.00 252.368457 3.729129
Moscou 0.665254 2486.34 571.197088 7.051030
Oslo 0.768169 1344.00 212.073108 5.222225
In [ ]:
df_moyen['Destination']=['Athenes','Londres','Madrid','Moscou','Oslo']

Diagramme à barre des prix moyens de chaque destination

In [ ]:
sns.barplot(x="Destination", y="Prix", data=df_moyen)
Out[ ]:
<AxesSubplot:xlabel='Destination', ylabel='Prix'>

ii) Comparaison globale (toile d'araignée)

In [ ]:
df_moyen= df.groupby(['Destination']).mean()
In [ ]:
df_moyen
Out[ ]:
Total d'escales Distance Prix Duree de vol
Destination
Athenes 0.742656 2095.52 287.960344 6.443462
Londres 0.350297 342.76 173.448249 2.475336
Madrid 0.400604 1054.00 252.368457 3.729129
Moscou 0.665254 2486.34 571.197088 7.051030
Oslo 0.768169 1344.00 212.073108 5.222225
In [ ]:
df_moyen.loc['Paris']=[0.001,0.001,0.001,0.001]
In [ ]:
x = df_moyen.values
min_max_scaler = preprocessing.MinMaxScaler()
x_scaled = min_max_scaler.fit_transform(x)
df_moyen= pd.DataFrame(x_scaled)
In [ ]:
df_moyen.columns = ["Total d'escales","Distance","Prix","Duree de vol"]
df_moyen.rename(index={0: 'Athenes',1:'Londres',2:'Madrid',3:'Moscou',4:'Oslo',5:'Paris' }, inplace=True)
In [ ]:
df_moyen #On a normalisé les valeursde 0 à 1 avec comme point initial Paris
Out[ ]:
Total d'escales Distance Prix Duree de vol
Athenes 0.966744 0.842813 0.504134 0.913821
Londres 0.455306 0.137857 0.303656 0.350968
Madrid 0.520881 0.423916 0.441823 0.528810
Moscou 0.865851 1.000000 1.000000 1.000000
Oslo 1.000000 0.540553 0.371277 0.740596
Paris 0.000000 0.000000 0.000000 0.000000
In [ ]:
df_moyen_transposed= df_moyen.T
In [ ]:
df_moyen_transposed
Out[ ]:
Athenes Londres Madrid Moscou Oslo Paris
Total d'escales 0.966744 0.455306 0.520881 0.865851 1.000000 0.0
Distance 0.842813 0.137857 0.423916 1.000000 0.540553 0.0
Prix 0.504134 0.303656 0.441823 1.000000 0.371277 0.0
Duree de vol 0.913821 0.350968 0.528810 1.000000 0.740596 0.0

Si la toile d'araignée ne s'affiche pas elle est exportée en PNG dans le dossier ('3. DataViz')

In [ ]:
fig = make_subplots(rows=1, cols=1, specs=[[{'type': 'polar'}]])

fig.add_trace(go.Scatterpolar(
        r = list(df_moyen_transposed['Athenes'])+ [list(df_moyen_transposed['Athenes'])[0]],
        theta = list(df_moyen_transposed.index)+["Total d'escales"],
        name = "Athènes"
        ), 1, 1)
fig.add_trace(go.Scatterpolar(
        r = list(df_moyen_transposed['Londres'])+ [list(df_moyen_transposed['Londres'])[0]],
        theta = list(df_moyen_transposed.index)+["Total d'escales"],
        name = "Londres"
        ), 1, 1)
fig.add_trace(go.Scatterpolar(
        r = list(df_moyen_transposed['Madrid'])+ [list(df_moyen_transposed['Madrid'])[0]],
        theta = list(df_moyen_transposed.index)+["Total d'escales"],
        name = "Madrid"
        ), 1, 1)
fig.add_trace(go.Scatterpolar(
        r = list(df_moyen_transposed['Moscou'])+ [list(df_moyen_transposed['Moscou'])[0]],
        theta = list(df_moyen_transposed.index)+["Total d'escales"],
        name = "Moscou"
        ), 1, 1)
fig.add_trace(go.Scatterpolar(
        r = list(df_moyen_transposed['Oslo'])+ [list(df_moyen_transposed['Oslo'])[0]],
        theta = list(df_moyen_transposed.index)+["Total d'escales"],
        name = "Oslo"
        ), 1, 1)
#fig.show()
In [ ]:
fig.write_image('Polar_Destinations.png')

Evolution selon les périodes

In [ ]:
df['Mois'] = df['Date_aller'].apply(lambda date:date.month)
df['Semaine'] = df['Date_aller'].apply(lambda date:date.week)
In [ ]:
fig, axes = plt.subplots(ncols=2, figsize=(16, 4))

ax1, ax2 = axes

im1 = df.groupby('Mois').mean()['Prix'].plot(ax = ax1, title = "Evolution du prix en fonction du mois")
im2 = df.groupby('Semaine').mean()['Prix'].plot(ax = ax2,title = "Evolution du prix en fonction des semaines" )

Caractérisation des compagnies

In [ ]:
df_compagnie= df.groupby(['Compagnie']).mean()
In [ ]:
df_compagnie= df_compagnie.sort_values(by = 'Prix')
In [ ]:
df_compagnie['Compagnie']=list(df_compagnie.index)
In [ ]:
sns.barplot(x="Compagnie", y="Prix", data=df_compagnie[:5])
Out[ ]:
<AxesSubplot:xlabel='Compagnie', ylabel='Prix'>